﻿using System;
using Microsoft.Xna.Framework;
using RainEngine.SceneManagement;
using RainEngine.Rendering;
using System.ComponentModel;
using Microsoft.Xna.Framework.Graphics;

namespace RainEngine.SceneNodes
{
    /// <summary>
    /// 场景中的相机
    /// </summary>
    public class CameraSceneNode : SceneNode
    {
        #region 构造函数和成员变量

        /// <summary>
        /// 视矩阵
        /// </summary>
        protected Matrix viewMatrix;
        
        /// <summary>
        /// 投影矩阵
        /// </summary>
        protected Matrix projectionMatrix;        

        /// <summary>
        /// nodes的位置属性
        /// </summary>
        protected WorldPose pose;

        /// <summary>
        /// 绕x, y和z轴的旋转角度
        /// </summary>
        protected Vector3 angleRotation;
        
        /// <summary>
        /// 相机观察目标
        /// </summary>
        protected Vector3 target;

        /// <summary>
        /// 相机的向上矢量
        /// </summary>
        protected Vector3 upVector;

        /// <summary>
        /// 真实相机包围视锥体
        /// </summary>
        protected BoundingFrustum internalFarPlaneBoundingFrustum;

        /// <summary>
        /// 逻辑相机包围视锥体，用于剔除。
        /// </summary>
        protected BoundingFrustum farPlaneBoundingFrustum;

        //剔除使用的方法，默认使用FarPlaneFrustum剔除
        protected CullingMethod cullMethod = CullingMethod.FarPlaneFrustum;

        /// <summary>
        /// 逻辑投影矩阵，用于剔除。
        /// </summary>
        protected Matrix farPM;
        
        /// <summary>
        /// 相机的真实远裁平面。超过这个距离的几何体会被投影矩阵剪裁。 
        /// 位于逻辑远裁平面和真实远裁平面间的物体会被SceneManager剔除。 
        /// 这个值通常比FarPlane大得多，初始化时设为逻辑远裁平面的10倍。
        /// </summary>
        protected float internalFarPlane;
        
        /// <summary>
        /// 相机视角
        /// </summary>
        protected float cameraFov = MathHelper.PiOver4;

        /// <summary>
        /// 近裁平面
        /// </summary>
        protected float nearPlane = 0.1f;
        
        /// <summary>
        /// 逻辑远裁平面，用于剪裁，默认为1000f。
        /// </summary>
        protected float farPlane = 1000f;

        /// <summary>
        /// 相机每秒的旋转角度
        /// </summary>
        protected float rotationSpeed = 90.0f;

        /// <summary>
        /// 相机每秒的移动距离
        /// </summary>
        protected float movementSpeed = 60.0f;

        /// <summary>
        /// 相机是否已经改变
        /// </summary>
        internal bool hasChanged;

        /// <summary>
        /// 创建一个新相机节点
        /// </summary>
        /// <param name="engine">引擎</param>
        /// <param name="setScene">所属场景</param>
        /// <param name="cameraPosition">相机位置</param>
        /// <param name="cameraTarget">相机观察目标</param>
        /// <param name="nearPlane">近裁平面</param>
        /// <param name="farPlane">逻辑远裁平面</param>
        internal CameraSceneNode(GraphicsDevice graphicsDevice,Scene setScene, Vector3 cameraPosition, Vector3 cameraTarget, float nearPlane, float farPlane): base(graphicsDevice,setScene)
        {
            this.nearPlane = nearPlane;
            this.farPlane = farPlane;
            
            pose = new WorldPose();
            pose.Node = this;
            AngleRotation = Vector3.Zero;   
            this.pose.SetPosition(ref cameraPosition);

            this.Target = cameraTarget;
            this.UpdateOrder = SceneNodeOrdering.Camera.GetValue();
            
            this.internalFarPlaneBoundingFrustum = new BoundingFrustum(Matrix.Identity);
            this.farPlaneBoundingFrustum = new BoundingFrustum(Matrix.Identity);
            

            //Scene.Camera = this;
        }
        /// <summary>
        /// 创建一个新相机节点，默认
        /// </summary>
        /// <param name="game">引擎</param>
        /// <param name="setScene">所属场景</param>
        /// <param name="cameraPosition">相机位置</param>
        /// <param name="cameraTarget">相机观察目标</param>
        public CameraSceneNode(GraphicsDevice graphicsDevice, Scene setScene, Vector3 cameraPosition, Vector3 cameraTarget) : this(graphicsDevice, setScene, cameraPosition, cameraTarget, 0.5f, 1000f) { }
        
        #endregion

        public override void Initialize()
        {
            internalFarPlane = farPlane * 10;
            UpdateViewMatrix(true);
        }
        
        #region 属性

        /// <summary>
        /// 获取或设置节点的旋转角度
        /// </summary> 
        [Browsable(false)]
        public Vector3 AngleRotation
        {
            get
            {
                return angleRotation;
            }
            set
            {
                angleRotation = new Vector3(value.X % 360, value.Y % 360, value.Z % 360);
            }
        }

        /// <summary>
        /// 获取或设置node的Pose
        /// </summary> 
        public WorldPose Pose
        {
            get { return pose; }
            set { pose = value; }
        }

        /// <summary>
        /// 获取相机是否已经改变了它的位置或观察目标
        /// </summary>        
        public bool HasChanged { get { return hasChanged; } }
        

        /// <summary>
        /// 返回相机的真实包围视锥体。它基于较大的真实远裁平面的值
        /// </summary>        
        public BoundingFrustum InternalFarPlaneBoundingFrustum { get { return internalFarPlaneBoundingFrustum; } }

        /// <summary>
        /// 返回逻辑包围视锥体。这个视锥体比真实视锥体小。
        /// </summary>        
        public BoundingFrustum FarPlaneBoundingFrustum { get { return farPlaneBoundingFrustum; } }

        /// <summary>
        /// 获取或设置相机每秒的旋转角度
        /// </summary>
        public float RotationSpeed
        {
            get { return rotationSpeed; }
            set { rotationSpeed = value; }
        }
        
        /// <summary>
        /// 获取或设置相机每秒的移动距离
        /// </summary>
        public float MovementSpeed
        {
            get { return movementSpeed; }
            set { movementSpeed = value; }
        }
        
        /// <summary>
        /// 返回或获取相机的视角
        /// </summary>
        public float FOV
        {
            get { return cameraFov; }
            set
            {
                cameraFov = value;
                UpdateViewMatrix(true);
            }
        }
        
        /// <summary>
        /// 返回或获取逻辑远裁平面。
        /// </summary>
        public float FarPlane
        {
            get { return farPlane; }
            set
            {
                farPlane = value;
                UpdateViewMatrix(true);
            }
        }

        /// <summary>
        /// 获取或设置投影矩阵的真实远裁平面。InternalFarPlane基于<see cref="FarPlane"/>的值进行计算。
        /// 如果使用<see cref="CullingMethod.FarPlaneFrustum"/> (这是默认的)则这个属性不应被设置。注意：更新<see cref="FarPlane"/>后需要重新计算InternalFarPlane!
        /// </summary>        
        public float InternalFarPlane
        {
            get { return internalFarPlane; }
            set
            {
                internalFarPlane = value;
                UpdateViewMatrix(true);
            }
        }

        /// <summary>
        /// 获取或设置相机的近裁平面。
        /// </summary>
        public float NearPlane
        {
            get { return nearPlane; }
            set
            {
                nearPlane = value;
                UpdateViewMatrix(true);
            }
        }
        
        /// <summary>
        /// 相机视场的长宽比
        /// </summary>
        public float AspectRatio
        {
            get { return graphicsDevice.Viewport.AspectRatio; }
        }

        /// <summary>
        /// 获取或设置相机的观察目标
        /// </summary>
        public Vector3 Target
        {
            get { return target; }
            set
            {
                target = value;
                Quaternion rot2target = Quaternion.CreateFromRotationMatrix(Matrix.CreateBillboard(Pose.Position, target, Vector3.Up, Vector3.Forward));
                Pose.Rotation = rot2target;
            }
        }

        /// <summary>
        /// 获取相机的向上矢量
        /// </summary>
        public Vector3 Up
        {
            get { return upVector; }
        }


        /// <summary>
        /// 返回相机的投影矩阵
        /// </summary>       
        public Matrix ProjectionMatrix
        {
            get { return projectionMatrix; }
        }

        /// <summary>
        /// 返回相机的视矩阵
        /// </summary>        
        public Matrix ViewMatrix
        {
            get { return viewMatrix; }
        }        

        /// <summary>
        /// 获取剔除信息
        /// </summary>
        public CullingInfo Culling
        {
            get
            {
                return new CullingInfo(cullMethod, internalFarPlaneBoundingFrustum, farPlane, ref pose.Position);
            }
        }
        /// <summary>
        /// 获取CullingInfo信息
        /// </summary>
        /// <param name="cullingInf"></param>
        public void GetCulling(CullingInfo cullingInf)
        {
            cullingInf.CullingMethod = cullMethod;
            cullingInf.BoundingFrustum = internalFarPlaneBoundingFrustum;
            cullingInf.CameraPosition = pose.Position;
            cullingInf.FarPlane = farPlane;
        }

        #endregion

        /// <summary>
        /// 返回视矩阵
        /// </summary>
        /// <param name="viewMatrix">视矩阵</param>
        public void GetViewMatrix(out Matrix viewMatrix) { viewMatrix = this.viewMatrix; }

        /// <summary>
        /// 返回视矩阵和投影矩阵
        /// </summary>
        /// <param name="view">视矩阵</param>
        /// <param name="projection">投影矩阵</param>
        public void GetViewProjection(out Matrix view, out Matrix projection)
        {
            view = viewMatrix;
            projection = projectionMatrix;
        }

        /// <summary>
        /// 返回视矩阵和投影矩阵
        /// </summary>
        /// <param name="view">视矩阵</param>
        /// <param name="projection">投影矩阵</param>
        public void GetViewProjectionPosition(out Matrix view, out Matrix projection,out Vector3 position)
        {
            view = viewMatrix;
            projection = projectionMatrix;
            position = pose.Position;
        }

        /// <summary>
        /// 返回由相机旋转四元数旋转过的向前向量并乘以速度，用于相机的前后移动。
        /// </summary>
        /// <param name="speed">速度</param>
        /// <returns></returns>
        public Vector3 GetMoveForwardVector(float speed)
        {
            Vector3 addVector = Vector3.Transform(Vector3.Forward, pose.Rotation);
            return addVector * speed;
        }
        
        /// <summary>
        /// 返回经由给定rotation四元数旋转过的给定direction的方向并乘以速度，相当于上一个GetMoveForwardVector方法的更加灵活的方法，用于相机的左右平移。
        /// </summary>
        /// <param name="rotation">旋转四元数</param>
        /// <param name="direction">方向</param>
        /// <param name="speed">速度</param>
        /// <returns></returns>
        public static Vector3 GetMoveVector(Quaternion rotation, Vector3 direction, float speed)
        {
            Vector3 addVector = Vector3.Transform(direction, rotation);
            return addVector * speed;
        }

        /// <summary>
        /// 返回一个Vector3，它的分量被设置为绕xyz轴旋转的欧拉角。
        /// </summary>
        /// <param name="q">四元数</param>
        /// <returns></returns>
        public static Vector3 QuaternionToEulerAngles(Quaternion q)
        {
            float yaw = (float)(Math.Atan(2 * ((q.X * q.Y) + (q.W * q.Z)) / ((q.W * q.W) + (q.X * q.X) - (q.Y * q.Y) - (q.Z * q.Z))));
            float pitch = (float)(Math.Asin(-2 * ((q.X * q.Z) - (q.W * q.Y))));
            float roll = (float)(Math.Atan(2 * ((q.W * q.X) + (q.Y * q.Z)) / ((q.W * q.W) - (q.X * q.X) - (q.Y * q.Y) + (q.Z * q.Z))));
            return new Vector3(yaw, pitch, roll);
        }

        /// <summary>
        /// 更新视矩阵，根据参数更新投影矩阵和远裁平面视锥体矩阵
        /// </summary>
        /// <param name="updateProjection">是否更新投影矩阵</param>
        public virtual void UpdateViewMatrix(bool updateProjection)
        {
            //根据pose.Rotation创建一个旋转矩阵，基于这个旋转矩阵计算相机的向上方向
            Matrix rotMx = Matrix.CreateFromQuaternion(pose.Rotation);
            upVector = Vector3.Transform(Vector3.Up, rotMx);
            
            //基于旋转矩阵计算相机的向前方向并设置相机的观察目标
            target = Vector3.Transform(Vector3.Forward, rotMx);
            target += pose.Position;
            //创建视矩阵
            viewMatrix = Matrix.CreateLookAt(pose.Position, target, upVector);

            //updateProjection为true则重新设置真实投影矩阵和逻辑投影矩阵
            if (updateProjection)
            {
                //  真实视锥体，基于很大的真实远裁平面           
                projectionMatrix = Matrix.CreatePerspectiveFieldOfView(cameraFov, AspectRatio , nearPlane, internalFarPlane);
                internalFarPlaneBoundingFrustum.Matrix = viewMatrix * projectionMatrix;

                //  基于逻辑远裁平面的逻辑视锥体。           
                farPM = Matrix.CreatePerspectiveFieldOfView(cameraFov, AspectRatio, nearPlane, FarPlane);
                farPlaneBoundingFrustum.Matrix = viewMatrix * farPM;            
            }
            hasChanged = true;
        }

        /// <summary>
        /// 如果相机位置发生改变则将HasChanged设置为true
        /// </summary>
        public override void OnPositionChange()
        {
            hasChanged = true;
        }
    }
}
